﻿using SuperX.Common.Collection;
using SuperX.SuperSocket.Socket.Transfer.Client.DownLoad.Command;
using SuperX.SuperSocket.Socket.Transfer.Client.UpLoad;
using SuperX.SuperSocket.Socket.Transfer.Extend;
using SuperX.SuperSocket.Socket.Transfer.Server;
using SuperSocket.ClientEngine;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using ErrorEventArgs = SuperSocket.ClientEngine.ErrorEventArgs;

namespace SuperX.SuperSocket.Socket.Transfer.Client.DownLoad
{
    public class DownLoadEngine : IDisposable
    {
        protected IClientCommandReader<TransferCommandInfo> CommandReader { get; private set; }

        public delegate void TransferCompleteEventHandler(EventArgs e);

        public delegate void TransferErrorEventHandler(TransferErrorEventArgs e);

        public delegate void TransferStepEventHandler(TransferStepEventArgs e);

        public delegate void TransferFileNotExistEventHandler(TransferFileNotExistEventArgs e);

        public event TransferCompleteEventHandler TransferComplete;

        public event TransferErrorEventHandler TransferError;

        public event TransferStepEventHandler TransferStep;

        public event TransferFileNotExistEventHandler TransferFileNotExist;

        internal object lockobj = new object();
        private string _fileName;
        private string _saveName;
        private TcpClientSession Client;
        private Dictionary<string, ICommand<DownLoadEngine, TransferCommandInfo>> m_CommandDict = new Dictionary<string, ICommand<DownLoadEngine, TransferCommandInfo>>(StringComparer.OrdinalIgnoreCase);
        internal FileStream m_fileStream;
        private AutoResetEvent m_OpenedEvent = new AutoResetEvent(false);
        internal AutoResetEvent m_EndEvent = new AutoResetEvent(false);
        private int PacketSize = 1024 * 30;
        private ConcurrentList<byte> _readBuffers;
        private int TransferStatus = 0;
        private bool _autoDelFile = false;

        //??0:初始化状态，1：CheckExist,3,OnUpLoad,开始上传，4，OnDoData，数据传输中，5，数据传输完成,6,已结束的标志
        private EndPoint _endPoint;

        internal FileInfo DownloadInfo;

        private Timer _timeoutTimer;
        public int TimeoutCount { get; set; } = 30 * 1000; //毫秒

        public void Init(string ip, int port, string serverFileName, string saveName, bool autoDelFile = false)
        {
            EndPoint endpoint = new IPEndPoint(IPAddress.Parse(ip), port);
            Init(endpoint, serverFileName, saveName, autoDelFile);
        }

        public void Init(EndPoint endPoint, string serverFileName, string saveName, bool autoDelFile = false)
        {
            _endPoint = endPoint;
            _fileName = serverFileName;
            _saveName = saveName;
            _autoDelFile = autoDelFile;
            var baseArray = new DownloadCommandBase[] {
                new DoCheckExist(),
                new DoData() };
            foreach (DownloadCommandBase base2 in baseArray)
            {
                m_CommandDict.Add(base2.Name, base2);
            }
            Client = new AsyncTcpSession();
            Client.ReceiveBufferSize = PacketSize + 4;
            Client.Error += new EventHandler<ErrorEventArgs>(client_Error);
            Client.DataReceived += new EventHandler<DataEventArgs>(client_DataReceived);
            Client.Connected += new EventHandler(client_Connected);
            Client.Closed += new EventHandler(client_Closed);
            CommandReader = new ClientLoadReader();
        }

        public void BeginDownLoad()
        {
            try
            {
                _readBuffers = new ConcurrentList<byte>();
                if (TransferStatus != 0)
                {
                    throw new Exception("状态错误");
                }
                Client.Connect(_endPoint);
                if (!m_OpenedEvent.WaitOne(5000))
                {
                    throw new Exception("连接失败");
                }
                var downloadInfo = new FileInfo();
                downloadInfo.FileName = _fileName;
                downloadInfo.SaveName = _saveName;
                downloadInfo.PacketSize = PacketSize;
                downloadInfo.DownloadAutoDelFile = _autoDelFile;
                byte[] data = SerializeHelp.Serialize(downloadInfo);
                SendData(UpLoadOP.DoCheckExist, data, 0, data.Length);
                _timeoutTimer = new Timer(TimerCallback, null, TimeoutCount, Timeout.Infinite);
            }
            catch (Exception exception)
            {
                OnTransferError(new TransferErrorEventArgs(exception));
            }
        }

        private void TimerCallback(object state)
        {
            var message = string.Format("下载文件超时{0}毫秒，文件名：{1}", TimeoutCount, _fileName);
            ConsoleLogHelper.Warn(message);
            OnTransferError(new TransferErrorEventArgs(new TimeoutException(message)));
        }

        internal void SendData(UpLoadOP opCode, byte[] data, int offset, int length)
        {
            if (Client == null)
                return;
            byte[] senddata = new byte[length + 5];
            senddata[0] = 0;
            senddata[1] = (byte)((int)opCode / 256);
            senddata[2] = (byte)((int)opCode % 256);
            senddata[3] = (byte)(length / 256);
            senddata[4] = (byte)(length % 256);
            Buffer.BlockCopy(data, offset, senddata, 5, length);
            Client.Send(senddata, 0, senddata.Length);
        }

        private void m_OnDataReceived(byte[] data, int offset, int length)
        {
            ConsoleLogHelper.Debug("m_OnDataReceived" + length + " / " + offset + " / " + _readBuffers.Count);
            var receiveData = new byte[length];
            Buffer.BlockCopy(data, offset, receiveData, 0, length);
            _readBuffers.AddRange(receiveData);
            while (_readBuffers.Count >= 4)
            {
                var len = _readBuffers[2] * 0x100 + _readBuffers[3] + 4;
                if (len > _readBuffers.Count)
                {
                    return;
                }
                int i = 0;
                try
                {
                    ConsoleLogHelper.Debug("m_OnDataReceived Handler:" + len + " / " + _readBuffers.Count);
                    i++;
                    var buffer = _readBuffers.GetAndRemoveRange(0, len);
                    i++;
                    TransferCommandInfo commandInfo = CommandReader.GetCommandInfo(buffer, 0, len, out _);
                    i++;
                    if (CommandReader.NextCommandReader != null)
                    {
                        i++;
                        CommandReader = CommandReader.NextCommandReader;
                    }
                    if (commandInfo != null)
                    {
                        i++;
                        ExecuteCommand(commandInfo);
                    }
                    else
                    {
                        ConsoleLogHelper.Warn("命令转换数据错误");
                    }
                }
                catch (Exception ex)
                {
                    ConsoleLogHelper.Warn($"{i} 数据错误: {ex.Message}");
                }
            }
        }

        private void ExecuteCommand(TransferCommandInfo commandInfo)
        {
            ICommand<DownLoadEngine, TransferCommandInfo> command;
            if (m_CommandDict.TryGetValue(commandInfo.Key, out command))
            {
                command.ExecuteCommand(this, commandInfo);
            }
        }

        internal void OnTransferStep(long _TotalLen, long _TransferLen, int _CurrentPacket)
        {
            _timeoutTimer?.Change(TimeoutCount, Timeout.Infinite);
            if (TransferStep != null)
            {
                TransferStep(new TransferStepEventArgs(_TotalLen, _TransferLen, _CurrentPacket));
            }
        }

        internal void OnTransferComplete(TransferCompleteEventArgs eventArgs)
        {
            if (TransferComplete != null)
                TransferComplete(eventArgs);
        }

        internal void OnTransferFileNotExist(TransferFileNotExistEventArgs eventArgs)
        {
            if (TransferFileNotExist != null)
                TransferFileNotExist(eventArgs);
        }

        private void client_Closed(object sender, EventArgs e)
        {
            m_EndEvent?.Set();
            //OnTransferError(new TransferErrorEventArgs(new TransferSessionAbortException("会话异常关闭")));
        }

        private void client_Connected(object sender, EventArgs e)
        {
            m_OpenedEvent?.Set();
        }

        private void client_DataReceived(object sender, DataEventArgs e)
        {
            m_OnDataReceived(e.Data, e.Offset, e.Length);
        }

        private void client_Error(object sender, ErrorEventArgs e)
        {
            OnTransferError(new TransferErrorEventArgs(e.Exception));
        }

        internal void OnTransferError(TransferErrorEventArgs e)
        {
            if (TransferError != null)
            {
                TransferError.Invoke(e);
            }
            Dispose();
        }

        internal void DoDownloadEnd()
        {
            //_readBuffers = null;
            if (m_fileStream != null)
            {
                m_fileStream?.Close();
                m_fileStream = null;
            }
            string saveName = Path.GetFullPath(DownloadInfo.SaveName);
            string saveTempName = saveName + Config.TempSuffix;
            string configName = saveName + Config.ConfigSuffix;
            if (File.Exists(saveName))
            {
                File.Delete(saveName);
            }
            ConsoleLogHelper.Debug("Client doend");
            File.Move(saveTempName, saveName);
            if (File.Exists(configName))
            {
                File.Delete(configName);
            }
            OnTransferComplete(new TransferCompleteEventArgs(DownloadInfo.ProjectId));
            Dispose();
        }

        public void Dispose()
        {
            _timeoutTimer?.Dispose();
            _timeoutTimer = null;
            CommandReader = null;
            m_CommandDict.Clear();
            _fileName = string.Empty;
            _saveName = string.Empty;
            if (m_fileStream != null)
            {
                m_fileStream.Close();
                m_fileStream = null;
            }
            TcpClientSession client = Client;
            if (client != null)
            {
                client.Error -= new EventHandler<ErrorEventArgs>(client_Error);
                client.DataReceived -= new EventHandler<DataEventArgs>(client_DataReceived);
                client.Connected -= new EventHandler(client_Connected);
                client.Closed -= new EventHandler(client_Closed);
                if (client.IsConnected)
                {
                    client.Close();
                }
                Client = null;
            }
            if (m_OpenedEvent != null)
            {
                m_OpenedEvent.Reset();
                m_OpenedEvent = null;
            }
            if (m_EndEvent != null)
            {
                m_EndEvent.Reset();
                m_EndEvent = null;
            }
        }
    }
}